﻿#ifndef TL_MATRIX_H
#define TL_MATRIX_H

#include <iostream>
#include "TL_Status.h"


/*
КОДЫ ОШИБОК:
1 - неизвестная ошибка
2 - недопустимый индекс
3 - матрица не создана
4 - матрица уже создана
5 - недопустимый размер матрицы
6 - значение не определено
*/


namespace TL {
    /* TL::Matrix */

    template<class T> class Matrix {
    private:
        T **m_Matrix; // матрица со значениями
        bool **m_DefMatrix; // матрица с флагами, указывающих, определено ли значение
        unsigned int m_RowCount;
        unsigned int m_ColumnCount;

    private:
        void Delete();
        void SetNull();

    public:
        Matrix();
        Matrix(unsigned int newSize);
        Matrix(unsigned int newRowSize, unsigned int newColumnSize);
        Matrix(T **srcArray, unsigned int newRowSize, unsigned int newColumnSize);
        Matrix(const Matrix<T> &matrix);
        ~Matrix();

    public:
        void AddColumn(unsigned int newColumnPoint, TL::Status *status);
        void AddRow(unsigned int newRowPoint, TL::Status *status);
        void Create(unsigned int newRowSize, unsigned int newColumnSize, TL::Status *status);
        T Get(unsigned int rowNumber, unsigned int columnNumber, TL::Status *status);
        unsigned int GetColumnCount();
        unsigned int GetRowCount();
        bool IsCreated() const;
        bool IsDefined(unsigned int rowNumber, unsigned int columnNumber, TL::Status *status);
        void RemoveColumn(unsigned int remColumn, TL::Status *status);
        void RemoveRow(unsigned int remRow, TL::Status *status);
        void Set(unsigned int rowNumber, unsigned int columnNumber, T newValue, TL::Status *status);
        void Unset(unsigned int rowNumber, unsigned int columnNumber, TL::Status *status);

    public:
        template<class T> friend std::ostream &operator<<(std::ostream &stream, Matrix<T> &matrix);
    };

    template<class T> void Matrix<T>::Delete()
    {
        for(unsigned int i = 0; i < m_RowCount; i++) {
            delete [] m_Matrix[i];
            delete [] m_DefMatrix[i];
        }

        delete [] m_Matrix;
        delete [] m_DefMatrix;
    }

    template<class T> void Matrix<T>::SetNull()
    {
        m_Matrix = 0;
        m_DefMatrix = 0;

        m_RowCount = 0;
        m_ColumnCount = 0;
    }

    template<class T> Matrix<T>::Matrix()
    {
        SetNull();
    }

    template<class T> Matrix<T>::Matrix(unsigned int newSize)
    {
        if(newSize) {
            m_Matrix = new T *[newSize];
            m_DefMatrix = new bool *[newSize];

            for(unsigned int i = 0; i < newSize; i++) {
                m_Matrix[i] = new T [newSize];
                m_DefMatrix[i] = new bool [newSize];

                memset(m_DefMatrix[i], 0, newSize * sizeof(bool));
            }

            m_RowCount = newSize;
            m_ColumnCount = newSize;
        } else {
            SetNull();
        }
    }

    template<class T> Matrix<T>::Matrix(unsigned int newRowSize, unsigned int newColumnSize)
    {
        if(newRowSize && newColumnSize) {
            m_Matrix = new T *[newRowSize];
            m_DefMatrix = new bool *[newRowSize];

            for(unsigned int i = 0; i < newRowSize; i++) {
                m_Matrix[i] = new T [newColumnSize];
                m_DefMatrix[i] = new bool [newColumnSize];

                memset(m_DefMatrix[i], 0, newColumnSize * sizeof(bool));
            }

            m_RowCount = newRowSize;
            m_ColumnCount = newColumnSize;
        } else {
            SetNull();
        }
    }

    template<class T> Matrix<T>::Matrix(T **srcArray, unsigned int newRowSize, unsigned int newColumnSize)
    {
        if(newRowSize && newColumnSize) {
            m_Matrix = new T *[newRowSize];
            m_DefMatrix = new bool *[newRowSize];

            for(unsigned int i = 0; i < newRowSize; i++) {
                m_Matrix[i] = new T [newColumnSize];
                m_DefMatrix[i] = new bool [newColumnSize];

                for(unsigned int j = 0; j < newColumnSize; j++)
                    m_Matrix[i][j] = srcArray[i][j];

                memset(m_DefMatrix[i], 1, newColumnSize * sizeof(bool));
            }

            m_RowCount = newRowSize;
            m_ColumnCount = newColumnSize;
        } else {
            SetNull();
        }
    }

    template<class T> Matrix<T>::Matrix(const Matrix<T> &matrix)
    {
        if(matrix.IsCreated()) {
            m_Matrix = new T *[matrix.m_RowCount];
            m_DefMatrix = new bool *[matrix.m_RowCount];

            for(unsigned int i = 0; i < matrix.m_RowCount; i++) {
                m_Matrix[i] = new T [matrix.m_ColumnCount];
                m_DefMatrix[i] = new bool [matrix.m_ColumnCount];

                for(unsigned int j = 0; j < matrix.m_ColumnCount; j++)
                    m_Matrix[i][j] = matrix.m_Matrix[i][j];

                memcpy(m_DefMatrix[i], matrix.m_DefMatrix[i], matrix.m_ColumnCount * sizeof(bool));
            }

            m_RowCount = matrix.m_RowCount;
            m_ColumnCount = matrix.m_ColumnCount;
        } else {
            SetNull();
        }
    }

    template<class T> Matrix<T>::~Matrix()
    {
        if(IsCreated()) {
            Delete();
        }
    }

    template<class T> void Matrix<T>::AddColumn(unsigned int newColumnPoint, TL::Status *status)
    {
        status->SetNull();

        if(!IsCreated()) {
            status->SetError(3);
            return;
        }

        if(newColumnPoint > m_ColumnCount) {
            status->SetError(2);
            return;
        }

        unsigned int i, j;

        T **tempMatrix = new T *[m_RowCount];
        bool **tempDefMatrix = new bool *[m_RowCount];

        for(i = 0; i < m_RowCount; i++) {
            tempMatrix[i] = new T [m_ColumnCount+1];
            tempDefMatrix[i] = new bool [m_ColumnCount+1];

            for(j = 0; j < newColumnPoint; j++) {
                tempMatrix[i][j] = m_Matrix[i][j];
                tempDefMatrix[i][j] = m_DefMatrix[i][j];
            }

            tempDefMatrix[i][j++] = false;

            while(j < (m_ColumnCount+1)) {
                tempMatrix[i][j] = m_Matrix[i][j-1];
                tempDefMatrix[i][j] = m_DefMatrix[i][j-1];

                j++;
            }
        }

        Delete();

        m_Matrix = tempMatrix;
        m_DefMatrix = tempDefMatrix;

        m_ColumnCount++;

        status->SetSuccess();
    }

    template<class T> void Matrix<T>::AddRow(unsigned int newRowPoint, TL::Status *status)
    {
        status->SetNull();

        if(!IsCreated()) {
            status->SetError(3);
            return;
        }

        if(newRowPoint > m_RowCount) {
            status->SetError(2);
            return;
        }

        unsigned int i;

        T **tempMatrix = new T *[m_RowCount+1];
        bool **tempDefMatrix = new bool *[m_RowCount+1];

        for(i = 0; i < newRowPoint; i++) {
            tempMatrix[i] = new T [m_ColumnCount];
            tempDefMatrix[i] = new bool [m_ColumnCount];

            for(unsigned int j = 0; j < m_ColumnCount; j++)
                tempMatrix[i][j] = m_Matrix[i][j];

            memcpy(tempDefMatrix[i], m_DefMatrix[i], m_ColumnCount * sizeof(bool));
        }

        tempMatrix[i] = new T [m_ColumnCount];
        tempDefMatrix[i] = new bool [m_ColumnCount];

        memset(tempDefMatrix[i], 0, m_ColumnCount * sizeof(bool));

        i++;

        while(i < (m_RowCount+1)) {
            tempMatrix[i] = new T [m_ColumnCount];
            tempDefMatrix[i] = new bool [m_ColumnCount];

            for(unsigned int j = 0; j < m_ColumnCount; j++)
                tempMatrix[i][j] = m_Matrix[i-1][j];

            memcpy(tempDefMatrix[i], m_DefMatrix[i-1], m_ColumnCount * sizeof(bool));

            i++;
        }

        Delete();

        m_Matrix = tempMatrix;
        m_DefMatrix = tempDefMatrix;

        m_RowCount++;

        status->SetSuccess();
    }

    template<class T> void Matrix<T>::Create(unsigned int newRowSize, unsigned int newColumnSize, TL::Status *status)
    {
        status->SetNull();

        if(IsCreated()) {
            status->SetError(4);
            return;
        }

        if(!newRowSize || !newColumnSize) {
            status->SetError(5);
            return;
        }

        m_Matrix = new T * [newRowSize];
        m_DefMatrix = new bool *[newRowSize];

        for(unsigned int i = 0; i < newRowSize; i++) {
            m_Matrix[i] = new T [newColumnSize];
            m_DefMatrix[i] = new bool [newColumnSize];

            memset(m_DefMatrix[i], 0, newColumnSize * sizeof(bool));
        }

        m_RowCount = newRowSize;
        m_ColumnCount = newColumnSize;

        status->SetSuccess();
    }

    template<class T> T Matrix<T>::Get(unsigned int rowNumber, unsigned int columnNumber, TL::Status *status)
    {
        status->SetNull();

        if(!IsCreated()) {
            status->SetError(3);
            return 0;
        }

        if(columnNumber >= m_ColumnCount) {
            status->SetError(2);
            return 0;
        }

        if(rowNumber >= m_RowCount) {
            status->SetError(2);
            return 0;
        }

        if(!IsDefined(rowNumber, columnNumber, status)) {
            status->SetError(6);
            return 0;
        }

        status->SetSuccess();

        return m_Matrix[rowNumber][columnNumber];
    }

    template<class T> unsigned int Matrix<T>::GetColumnCount()
    {
        return m_ColumnCount;
    }

    template<class T> unsigned int Matrix<T>::GetRowCount()
    {
        return m_RowCount;
    }

    template<class T> bool Matrix<T>::IsCreated() const
    {
        return (m_RowCount > 0 && m_ColumnCount > 0);
    }

    template<class T> bool Matrix<T>::IsDefined(unsigned int rowNumber, unsigned int columnNumber, TL::Status *status)
    {
        status->SetNull();

        if(!IsCreated()) {
            status->SetError(3);
            return false;
        }

        if(columnNumber >= m_ColumnCount) {
            status->SetError(2);
            return false;
        }

        if(rowNumber >= m_RowCount) {
            status->SetError(2);
            return false;
        }

        status->SetSuccess();

        return m_DefMatrix[rowNumber][columnNumber];
    }

    template<class T> void Matrix<T>::RemoveColumn(unsigned int remColumn, TL::Status *status)
    {
        status->SetNull();

        if(!IsCreated()) {
            status->SetError(3);
            return;
        }

        if(remColumn >= m_ColumnCount) {
            status->SetError(2);
            return;
        }

        if(m_ColumnCount > 1) {
            unsigned int i, j, k;

            T **tempMatrix = new T *[m_RowCount];
            bool **tempDefMatrix = new bool *[m_RowCount];

            for(i = 0; i < m_RowCount; i++) {
                tempMatrix[i] = new T [m_ColumnCount-1];
                tempDefMatrix[i] = new bool [m_ColumnCount-1];

                for(j = 0, k = 0; j < m_ColumnCount; j++) {
                    if(j == remColumn)
                        continue;

                    tempMatrix[i][k] = m_Matrix[i][j];
                    tempDefMatrix[i][k++] = m_DefMatrix[i][j];
                }
            }

            Delete();

            m_Matrix = tempMatrix;
            m_DefMatrix = tempDefMatrix;

            m_ColumnCount--;
        } else {
            Delete();

            SetNull();
        }

        status->SetSuccess();
    }

    template<class T> void Matrix<T>::RemoveRow(unsigned int remRow, TL::Status *status)
    {
        status->SetNull();

        if(!IsCreated()) {
            status->SetError(3);
            return;
        }

        if(remRow >= m_RowCount) {
            status->SetError(2);
            return;
        }

        if(m_RowCount > 1) {
            unsigned int i, j, k;

            T **tempMatrix = new T *[m_RowCount-1];
            bool **tempDefMatrix = new bool *[m_RowCount-1];

            for(i = 0, k = 0; i < m_RowCount; i++) {
                if(i == remRow)
                    continue;

                tempMatrix[k] = new T [m_ColumnCount];
                tempDefMatrix[k] = new bool [m_ColumnCount];

                for(j = 0; j < m_ColumnCount; j++) {
                    tempMatrix[k][j] = m_Matrix[i][j];
                    tempDefMatrix[k][j] = m_DefMatrix[i][j];
                }

                k++;
            }

            Delete();

            m_Matrix = tempMatrix;
            m_DefMatrix = tempDefMatrix;

            m_RowCount--;
        } else {
            Delete();

            SetNull();
        }

        status->SetSuccess();
    }

    template<class T> void Matrix<T>::Set(unsigned int rowNumber, unsigned int columnNumber, T newValue, TL::Status *status)
    {
        status->SetNull();

        if(!IsCreated()) {
            status->SetError(3);
            return;
        }

        if(rowNumber >= m_RowCount) {
            status->SetError(2);
            return;
        }

        if(columnNumber >= m_ColumnCount) {
            status->SetError(2);
            return;
        }

        m_Matrix[rowNumber][columnNumber] = newValue;
        m_DefMatrix[rowNumber][columnNumber] = true;

        status->SetSuccess();
    }

    template<class T> void Matrix<T>::Unset(unsigned int rowNumber, unsigned int columnNumber, TL::Status *status)
    {
        status->SetNull();

        if(!IsCreated()) {
            status->SetError(3);
            return;
        }

        if(rowNumber >= m_RowCount) {
            status->SetError(2);
            return;
        }

        if(columnNumber >= m_ColumnCount) {
            status->SetError(2);
            return;
        }

        m_DefMatrix[rowNumber][columnNumber] = false;

        status->SetSuccess();
    }

    template<class T> std::ostream &operator<<(std::ostream &stream, Matrix<T> &matrix)
    {
        TL::Status status;

        for(unsigned int i = 0; i < matrix.m_RowCount; i++) {
            for(unsigned int j = 0; j < matrix.m_ColumnCount; j++) {
                if(matrix.IsDefined(i, j, &status)) {
                    stream << matrix.Get(i, j, &status) << " ";
                } else {
                    stream << "{} ";
                }
            }

            stream << "\n";
        }

        return stream;
    }
}

#endif